package assignment;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

/**
 * Encapsulates the writing of JAXB XML to a output stream.
 * @author $Author:$
 * @version $Id: $
 */
public class XMLOutputter {
	private final String encoding;
	private OutputStreamWriter writer;
	private List<String> schemaLocations = new ArrayList<String>();

	/**
	 * Creates an XMLOutputter, a control object to aid on writing JAXB XML to a stream.
	 * @param out the stream to write to
	 * @param encoding the encoding to use while writing.
	 * @throws UnsupportedEncodingException
	 */
	public XMLOutputter(OutputStream out,String encoding) throws UnsupportedEncodingException{
		this.encoding = encoding;
		writer = new OutputStreamWriter(out,encoding);
	}
	
	/**
	 * Writes a JAXB object to this XMLOutputter output stream. The annotations in the class and the package of the object
	 * must hold enough information to create a JAXBContext.
	 * @param o the object to be written
	 * @param format should the xml be formated?
	 * @throws IOException
	 * @throws JAXBException
	 */
	public void write(Object o,boolean format) throws IOException, JAXBException {
		//Initialize a JAXB context from the class of the jaxb object
		JAXBContext context = JAXBContext.newInstance(o.getClass()); 
		Marshaller marshaller = context.createMarshaller();
		
		marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,format);//set the format acordingly to users choice
		//disables the writing of the fragment (<?xml...?>). The user must explicitly call writeFragment() if he requires the fragment.
		marshaller.setProperty(Marshaller.JAXB_FRAGMENT,true);
		for(String sloc: schemaLocations)
			marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, sloc);
		marshaller.marshal(o, writer);
	}
	
	/**
	 * Adds a schema locations to be written when a write(Object,boolean) is called.
	 * @param namespace the namespace of the schema 
	 * @param location the location of the schema
	 */
	public void addSchemaLocation(String namespace,String location){
		schemaLocations.add(namespace+" "+location);
	}

	/**
	 * Writes a <?xml-stylesheet...?> processing instruction on the stream.
	 * @param template the location of the xslt template to be applied to the document
	 * @throws IOException
	 */
	public void writeXSLTTemplateReference(String template) throws IOException {
		writer.write("<?xml-stylesheet type=\"text/xsl\" href=\""+template+"\"?>\n");
	}

	/**
	 * Writes a <?xml...?> processing instruction (also known as fragment) on the stream.
	 * @throws IOException 
	 */
	public void writeFragment() throws IOException {
		writer.write("<?xml version=\"1.0\" encoding=\""+encoding+"\"?>\n");
	}

}
